﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using JobManager.Exceptions;

namespace JobManager
{
    public class Job
    {
        #region private members
        private string _userID;
        private string _jobID;

        private static string GetStatusFromFile(string aUserID, string aJobID, string aStatusFileName)
        {
            string retval = String.Empty;

            string lstatusFileFullPath = Path.Combine(JobFolder(aUserID, aJobID), aStatusFileName);

            using (new JobScope(aUserID, aJobID))
            {
                if (File.Exists(lstatusFileFullPath))
                {
                    retval = File.ReadAllText(lstatusFileFullPath);
                }
            }

            return retval;
        }

        private static void SetStatusInFile(string aUserID, string aJobID, string aStatusFileName, JobStatus aStatus, string aMessage)
        {
            string lstatusFileFullPath = Path.Combine(JobFolder(aUserID, aJobID), aStatusFileName);

            using (new JobScope(aUserID, aJobID))
            {
                string lcontent = aStatus.ToString();
                if (!String.IsNullOrEmpty(aMessage))
                    lcontent += ":" + aMessage;

                File.WriteAllText(lstatusFileFullPath, lcontent); 
            }
        }

        private static JobStatus ParseStatus(string aStatus)
        {
            JobStatus retval = JobStatus.Created;

            if (!String.IsNullOrEmpty(aStatus))
            {
                string[] lstatusArr = aStatus.Split(':');

                try
                {
                    retval = (JobStatus)Enum.Parse(typeof(JobStatus), lstatusArr[0], true);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.Fail(ex.ToString());
                }
            }

            return retval;
        }

        private static string ParseStatusMessage(string aStatus)
        {
            string retval = null;

            if (!String.IsNullOrEmpty(aStatus))
            {
                string[] lstatusArr = aStatus.Split(':');

                for (int t = 1; t < lstatusArr.Length; t++)
                {
                    if (retval == null)
                        retval = lstatusArr[t];
                    else
                        retval += lstatusArr[t];
                }
            }

            return retval;
        }

        private bool CheckPrerequisitesForProcessing(out string lmessage)
        {
            // stub
            lmessage = null;

            return true;
        }

        private void CreateJob(string aUserID, string aJobID)
        {
            string ljobFolder = JobFolder(aUserID, aJobID);

            using (new JobScope(aUserID, aJobID))
            {
                if (!Directory.Exists(ljobFolder))
                    Directory.CreateDirectory(ljobFolder);
            }
        }

        #endregion

        #region constructors
        
        public Job(string aUserID)
        {
            if (String.IsNullOrEmpty(aUserID))
                throw new ArgumentNullException("aUserID");

            _userID = aUserID;
            _jobID = Guid.NewGuid().ToString();

            CreateJob(_userID, _jobID);
        }

        public Job(string aUserID, string aJobID)
        {
            if (String.IsNullOrEmpty(aUserID))
                throw new ArgumentNullException("aUserID");

            using (new JobScope(aUserID, aJobID))
            {
                if (!Exists(aUserID, aJobID))
                {
                    CreateJob(aUserID, aJobID);
                }
            }

            _userID = aUserID;

            _jobID = aJobID;
        }

        #endregion

        #region public members

        public const string CStatusFileName = "StatusFile._j_";

        public static string BaseFolder()
        {
            return Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                "JobManager"
                );
        }

        public static string UserFolder(string aUserID)
        {
            if (String.IsNullOrEmpty(aUserID))
                throw new ArgumentNullException("aUserID");
            
            return Path.Combine(
                BaseFolder(),
                "User-" + aUserID
                );
        }

        public static string JobFolder(string aUserID, string aJobID)
        {
            if (String.IsNullOrEmpty(aJobID))
                throw new ArgumentNullException("aJobID");

            return Path.Combine(
                UserFolder(aUserID),
                "Job-" + aJobID
                );
        }

        public static bool Exists(string aUserID, string aJobID)
        {
            return Directory.Exists(JobFolder(aUserID, aJobID));
        }

        public string UserID { get { return _userID; } }
        public string JobID { get { return _jobID; } }

        public JobStatus Status
        {
            get
            {
                string lstatusString = GetStatusFromFile(UserID, JobID, CStatusFileName);

                JobStatus retval = ParseStatus(lstatusString);

                if (retval == JobStatus.Created)
                {
                    // need to check if we are actually ReadyForProcessing
                    // implement this
                }

                return retval;
            }
        }

        public string StatusMessage
        {
            get
            {
                string retval;

                string lstatusString = GetStatusFromFile(UserID, JobID, CStatusFileName);

                retval = ParseStatusMessage(lstatusString);
                
                return retval;
            }
        }

        public void SubmitForProcessing()
        {
            using (new JobScope(UserID, JobID))
            {
                if (Status == JobStatus.Created)
                {
                    string lmessage;

                    if (!CheckPrerequisitesForProcessing(out lmessage))
                    {
                        throw new BadPrerequisitesException(lmessage);
                    }
                    else
                    {
                        SetStatusInFile(UserID, JobID, CStatusFileName, JobStatus.ReadyForProcessing, null);
                    }
                }
                else
                    throw new BadStatusException("Status should be Created, but is " + Status.ToString());
            }
        }

        public void CancelJob()
        {
            using (new JobScope(UserID, JobID))
            {
                JobStatus lstatus = Status;

                if (lstatus != JobStatus.Succeeded && lstatus != JobStatus.Failed)
                {
                    SetStatusInFile(UserID, JobID, CStatusFileName, JobStatus.Failed, "Cancelled by user");
                }
            }
        }

        #endregion
    }
}
